/*
SPDX-License-Identifier: Apache-2.0
*/

package main

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/golang/protobuf/proto"
	"github.com/hyperledger/fabric-contract-api-go/contractapi"
	"github.com/hyperledger/fabric-protos-go/msp"
)

type SmartContract struct {
	contractapi.Contract
}

type Data struct {
	State float64 `json:"state"`
	OrgID string  `json:"orgid"`
}

type QueryResult struct {
	Key    string `json:"Key"`
	Record *Data
}

// 权限判断
func permissionChecker(MSPID string, perm int64) bool {
	if perm == 1 {
		return MSPID == "Org1MSP"
	} else if perm == 2 {
		return MSPID == "Org1MSP" || MSPID == "Org2MSP"
	} else if perm == 3 {
		return true
	} else {
		return false
	}
}

func (s *SmartContract) CreatePerson(ctx contractapi.TransactionContextInterface, ID string, state float64) error {
	creatorByte, err := ctx.GetStub().GetCreator()
	si := &msp.SerializedIdentity{}
	err = proto.Unmarshal(creatorByte, si)
	if err != nil {
		return err
	}

	if !permissionChecker(si.GetMspid(), 1) {
		return fmt.Errorf("Permission Denied")
	}

	checker, err := ctx.GetStub().GetState(ID)

	if checker != nil {
		return fmt.Errorf("%s already exist", ID)
	}

	data := Data{
		State: state,
		OrgID: si.GetMspid(),
	}

	dataAsBytes, _ := json.Marshal(data)

	return ctx.GetStub().PutState(ID, dataAsBytes)
}

func (s *SmartContract) QueryState(ctx contractapi.TransactionContextInterface, ID string) (*Data, error) {
	creatorByte, err := ctx.GetStub().GetCreator()
	si := &msp.SerializedIdentity{}
	err = proto.Unmarshal(creatorByte, si)
	if err != nil {
		return nil, err
	}

	if !permissionChecker(si.GetMspid(), 3) {
		return nil, fmt.Errorf("Permission Denied")
	}

	dataAsBytes, err := ctx.GetStub().GetState(ID)

	if err != nil {
		return nil, fmt.Errorf("Failed to read from world state. %s", err.Error())
	}

	if dataAsBytes == nil {
		return nil, fmt.Errorf("%s does not exist", ID)
	}

	data := new(Data)
	_ = json.Unmarshal(dataAsBytes, data)

	return data, nil
}

func (s *SmartContract) QueryAllStates(ctx contractapi.TransactionContextInterface) ([]QueryResult, error) {
	creatorByte, err := ctx.GetStub().GetCreator()
	si := &msp.SerializedIdentity{}
	err = proto.Unmarshal(creatorByte, si)
	if err != nil {
		return nil, err
	}

	if !permissionChecker(si.GetMspid(), 2) {
		return nil, fmt.Errorf("Permission Denied")
	}

	startKey := ""
	endKey := ""

	resultsIterator, err := ctx.GetStub().GetStateByRange(startKey, endKey)

	if err != nil {
		return nil, err
	}
	defer resultsIterator.Close()

	results := []QueryResult{}

	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()

		if err != nil {
			return nil, err
		}

		ret := new(Data)
		_ = json.Unmarshal(queryResponse.Value, ret)

		queryResult := QueryResult{Key: queryResponse.Key, Record: ret}
		results = append(results, queryResult)
	}

	return results, nil
}

func (s *SmartContract) ChangePersonState(ctx contractapi.TransactionContextInterface, ID string, state float64) error {
	creatorByte, err := ctx.GetStub().GetCreator()
	si := &msp.SerializedIdentity{}
	err = proto.Unmarshal(creatorByte, si)
	if err != nil {
		return err
	}

	if !permissionChecker(si.GetMspid(), 2) {
		return fmt.Errorf("Permission Denied")
	}

	data, err := s.QueryState(ctx, ID)

	if err != nil {
		return err
	}

	data.State = state
	data.OrgID = si.GetMspid()

	dataAsBytes, _ := json.Marshal(data)

	return ctx.GetStub().PutState(ID, dataAsBytes)
}

// 查找历史记录
func (s *SmartContract) QueryHistory(ctx contractapi.TransactionContextInterface, ID string) ([]QueryResult, error) {
	creatorByte, err := ctx.GetStub().GetCreator()
	si := &msp.SerializedIdentity{}
	err = proto.Unmarshal(creatorByte, si)
	if err != nil {
		return nil, err
	}

	if !permissionChecker(si.GetMspid(), 2) {
		return nil, fmt.Errorf("Permission Denied")
	}

	resultsIterator, err := ctx.GetStub().GetHistoryForKey(ID)

	if err != nil {
		return nil, err
	}

	defer resultsIterator.Close()

	results := []QueryResult{}

	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()

		if err != nil {
			return nil, err
		}

		data := new(Data)
		_ = json.Unmarshal(queryResponse.Value, data)

		tm := time.Unix(queryResponse.Timestamp.Seconds+3600*8, 0)

		queryResult := QueryResult{Key: tm.Format("2006-01-02 15:04:05 PM"), Record: data}
		results = append(results, queryResult)
	}

	return results, nil
}

func main() {

	chaincode, err := contractapi.NewChaincode(new(SmartContract))

	if err != nil {
		fmt.Printf("创建链码时发生错误: %s", err.Error())
		return
	}

	if err := chaincode.Start(); err != nil {
		fmt.Printf("开始链码时发生错误: %s", err.Error())
	}
}
